package com.example.demo.demoApi;

import com.example.demo.demoApi.apiHandler.ApiMsgTypeHandler;
import com.example.demo.demoApi.apiHandler.ApiSelector;
import com.example.demo.demoApi.header.ResponseHeader;
import com.example.demo.demoApi.msg.AbstractMsg;
import com.example.demo.demoApi.msg.ExternalMsg;
import com.example.demo.demoApi.msg.UnparsableResponseMsg;
import com.example.demo.exception.APIException;
import com.example.demo.redis.OperateService;
import com.example.demo.typeEnums.APIProtocolType;
import com.example.demo.typeEnums.APIResult;
import com.example.demo.utils.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class ExternalExecutor implements NettyExecutor {

    @Autowired
    private ApiSelector apiSelector;

    @Autowired
    private OperateService operateService;

    /**
     * 执行外部请求
     *
     * @param protoType  消息来源协议类型
     * @param remoteAddr 消息来源地址
     */
    public synchronized AbstractMsg execute(AbstractMsg msg, APIProtocolType protoType, String remoteAddr) {

        if (msg instanceof UnparsableResponseMsg) {
            return msg;
        }else if(msg instanceof ExternalMsg){
            try {
                synchronized(AbstractMsg.class) {
                    AbstractMsg respMsg = null;
                    respMsg = this.handleIdempotence(msg);
                    if (respMsg != null) {
                        return respMsg;
                    }
                }
                return handlerMsg(msg);
            }catch (Exception e) {
                log.error("save msg failed! protoType:{}, remoteAddr:{}, msg:{}", protoType, remoteAddr, msg);
                return newUnparsableMsg(msg.toString(), e);
            }
        }
        return newUnparsableMsg(msg.toString(), null);
    }

    /**
     * 将消息转换为java bean
     *
     * @param content    消息内容
     * @return 响应消息
     */
    public synchronized AbstractMsg parseToMsg(String content) {
        try {
            JsonNode jsonNode = JSONUtil.jsonToNode(content);

            verifyMsgBasic(jsonNode);

            ExternalMsg msg = JSONUtil.nodeToObj(jsonNode, getMsgClassByType(jsonNode.get("body").get("msgType").asText()));
            msg.setId(msg.getHeader().getChannelId());
            msg.setChannelId(msg.getHeader().getChannelId());
            msg.setRequestId(msg.getHeader().getRequestId());
            msg.setMsgType(msg.getBody().getMsgType());
            msg.setContent(content);
            return msg;

        } catch (Exception e) {
            log.error("parse msg failed! msg:{}",content);
            return newUnparsableMsg(content, e);
        }

    }


    /**
     * 对消息的基本信息进行验证（非业务）
     */
    private void verifyMsgBasic(JsonNode jsonNode) {
        // parse to java bean
        if (!jsonNode.has("header") || jsonNode.get("header").isNull()) {
            throw new APIException(APIResult.REQUEST_MSG_HEADER_NULL);
        }
        if (!jsonNode.get("header").has("requestId") || StringUtils.isEmpty(jsonNode.get("header").get("requestId").asText())) {
            throw new APIException(APIResult.REQUEST_MSG_REQUESTID_NULL);
        }
        if (!jsonNode.get("header").has("channelId") || StringUtils.isEmpty(jsonNode.get("header").get("channelId").asText())) {
            throw new APIException(APIResult.REQUEST_CHANNEL_ID_NULL);
        }
        if (!jsonNode.has("body") || jsonNode.get("body").isNull()) {
            throw new APIException(APIResult.REQUEST_MSG_BODY_NULL);
        }
        if (!jsonNode.get("body").has("msgType") || StringUtils.isEmpty(jsonNode.get("body").get("msgType").asText())) {
            throw new APIException(APIResult.REQUEST_MSG_TYPE_NULL);
        }
    }

    /**
     * 创建不支持的响应消息
     * @param content 请求消息类型
     * @return 不支持的消息
     */
    public UnparsableResponseMsg newUnparsableMsg(String content, Exception e) {
        ResponseHeader header = new ResponseHeader();
        if (e instanceof APIException) {
            APIException apiException = (APIException) e;
            header.setCode(apiException.getCode());
            header.setMsg(apiException.getMsg());
            header.setArgs(apiException.getArgs());
        } else if (StringUtils.isEmpty(content)) {
            header.setCode(APIResult.REQUEST_MSG_NULL.getCode());
            header.setMsg(APIResult.REQUEST_MSG_NULL.getMsg());
        } else {
            header.setCode(APIResult.REQUEST_MSG_ILLEGAL.getCode());
            header.setMsg(APIResult.REQUEST_MSG_ILLEGAL.getMsg());
        }
        if (StringUtils.isNotBlank(content)) {
            JsonNode jsonNode = JSONUtil.jsonToNode(content);
            if (null != jsonNode) {
                JsonNode headerNode = jsonNode.get("header");
                if (null != headerNode) {
                    header.setResponseId(headerNode.get("requestId").textValue());
                }
            }
        }

        UnparsableResponseMsg msg = new UnparsableResponseMsg();
        msg.setMsgType(UnparsableResponseMsg.class.getSimpleName());
        msg.setRpsheader(header);

        return msg;
    }


    public Class<? extends ExternalMsg> getMsgClassByType(String msgType){
        return apiSelector.selectMsgClass(msgType);
    }

    public AbstractMsg handlerMsg(AbstractMsg msg){
        AbstractMsg abstractMsg = null;
        try {
            ApiMsgTypeHandler handler = apiSelector.selectApiHandler(msg.getClass());
            if(handler != null) {
                abstractMsg = handler.handle(msg);
            }
            String requestId = msg.getId();
            operateService.putMapValue(requestId,requestId,abstractMsg,5L, TimeUnit.MINUTES);
        }catch (Exception e){
            log.error("api execute error!","exception",e);
        }finally {

        }
        return abstractMsg;
    }

    /**
     * 幂等性处理
     */
    public AbstractMsg handleIdempotence(AbstractMsg msg){
        if(msg instanceof ExternalMsg) {
            String requestId = ((ExternalMsg) msg).getRequestId();
            Object responseMsg = operateService.getMapValue(requestId, requestId);
            return (AbstractMsg) responseMsg;
        }
        return null;
    }


}
